#include "MetaClasse.h"

#include "lib.h"

#include "ATL.h"

//#include "debugstreams.h"

#include <vector>

#include "Guerrier.h"
#include "Paladin.h"
#include "Rodeur.h"
#include "Clerc.h"
#include "Druide.h"
#include "Mage.h"
#include "Specialiste.h"
#include "Barde.h"
#include "Voleur.h"

#include "CodesErreurs.h"

using namespace std;

const string MetaClasse::fichierSource( "Joueur0/Donnees/ListeClasse.xml" );

/**
 * Constructeur par standard. Uniquement le nom de la Classe est
 * initialisé ici. Les autres informations sur la Classe sont lues
 * depuis le fichier par la méthode lireFichier().
 *
 * @param[in] nomClasse nom de la classe.
 */
MetaClasse::MetaClasse(const string& nomClasse)
  : Classe(), Lecture( fichierSource )
{
  NomClasse = nomClasse;
}

/**
 * Le destructeur n'a rien de spécial à faire.
 */
MetaClasse::~MetaClasse()
{}

/**
 * Réimplémentation nécessaire pour la compilation, mais ne doit
 * jamais être appelée.
 */
void MetaClasse::calculNiveau()
{
  warning() << "MetaClasse::calculNiveau() ne devrait pas être appelée"
	    << endmsg;
}

/**
 * Réimplémentation nécessaire pour la compilation, mais ne doit
 * jamais être appelée.
 */
unsigned MetaClasse::calculeTAc0() const
{
  warning() << "MetaClasse::calculeTAc0() ne devrait pas être appelée"
	    << endmsg;
  
  return 21;
}

/**
 * Réimplémentation nécessaire pour la compilation, mais ne doit
 * jamais être appelée.
 */
void MetaClasse::calculNivMag()
{
  warning() << "MetaClasse::calculNivMag() ne devrait pas être appelée"
	    << endmsg;
}

/**
 * Réimplémentation nécessaire pour la compilation, mais ne doit
 * jamais être appelée.
 */
void MetaClasse::js(unsigned*) const
{
  warning() << "MetaClasse::js() ne devrait pas être appelée" << endmsg;
}

/**
 * Réimplémentation nécessaire pour la compilation, mais ne doit
 * jamais être appelée.
 */
void MetaClasse::prixAcces(map< string, unsigned >*&) const
{
  warning() << "MetaClasse::prixAcces() ne devrait pas être appelée"
	    << endmsg;
}

/**
 * Lit le fichier et charge la Classe correspondante dans l'instance
 * courante.
 *
 * Le niveau de sortie est paramétré par Classe::elementaire.\n
 * -\e FUNC affiche les messages de début et de fin de méthode.
 * -\e INFO affiche les différentes informations lues depuis le
    fichier.\n
 *
 * @return StatusCode, en cas d'erreur le message permet d'identifier
 * le problème.
 */
StatusCode MetaClasse::lireFichier()
{
  /*Debug::*/empileNiveau( &elementaire );
  func() << "lireFichier ==> début" << endmsg;

  StatusCode sc( Lecture.lireDocument() );
  if ( ! sc )
    {
      func() << "lireFichier ==> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return sc;
    }

  const ElementDOM* noeud;

  noeud = Lecture.premierEnfant( Lecture.premierEnfant() );

  if ( noeud == 0 )
    {
      func() << "lireFichier ==> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return StatusCode( false, 0, - CodesErreurs::AucunEnfantTrouve,
			 "Aucun enfant trouvé" );
    }

  do
    {
      if ( noeud -> balise() != "Classe" )
	{
	  func() << "lireFichier ==> fin" << endmsg;
	  /*Debug::*/depileNiveau();
	  return StatusCode( false, 0, - CodesErreurs::AutreBaliseTrouvee,
			     "On attendait \"Classe\"" );
	}

      if ( Lecture.premierEnfant( noeud ) -> donnees() == NomClasse )
	break;

      noeud = Lecture.prochainFrere( noeud );
    }
  while ( noeud != 0 );

  if ( noeud == 0 )
    {
      func() << "lireFichier ==> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return StatusCode( false, 0, - CodesErreurs::ElementManquant,
			 "Impossible de trouver " + NomClasse );
    }

  noeud = Lecture.premierEnfant( noeud );

  info() << noeud -> balise() << " : " << noeud -> donnees() << endmsg;

  noeud = Lecture.prochainFrere( noeud );

  if ( noeud -> donnees() == "Combattants" )
    TC.push_back( Combattants );
  else if ( noeud -> donnees() == "Pretres" )
    TC.push_back( Pretres );
  else if ( noeud -> donnees() == "Magiciens" )
    TC.push_back( Magiciens );
  else if ( noeud -> donnees() == "Roublards" )
    TC.push_back( Roublards );

  noeud = Lecture.prochainFrere( noeud );

  info() << "Points alloués pour la création : " << noeud -> donnees()
	 << endmsg;

  if ( ! lireString( noeud -> donnees(), NombrePPsAlloues ) )
    {
      error() << "Ne peut pas lire le nombre de PP allouées pour la"
	      << " création" << endmsg;
      return StatusCode( false, 0, - CodesErreurs::ErreurLectureScalaireUint,
			 "lireFichier : Impossible de lire le nombre de PP alloués" );
    }

  noeud = Lecture.prochainFrere( noeud );

  unsigned nbrElement( 0 ), uIntTmp, elementsAttendus( 0 );

  do
    {
      if ( noeud -> attribut( "groupe" ) != "" )
	info() << "Nous lisons le sous groupe numéro"
	       << noeud -> attribut( "groupe" ) << endmsg;

      if ( ! lireString( noeud -> attribut( "nombre" ), elementsAttendus ) )
	{
	  error() << "Ne peut pas lire le nombre de races admises" << endmsg;
	  func() << "lireFichier ==> fin" << endmsg;
	  /*Debug::*/depileNiveau();
	  return StatusCode( false, 0, - CodesErreurs::ErreurLectureAttributUint,
			     "lireFichier : Impossible de lire le nombre d'élément attendus" );
	}

      const ElementDOM* admise( Lecture.premierEnfant( noeud ) );
      RacesAdmisesClasse.push_back( set< string >() );

      do
	{
	  if ( ! lireString( admise -> attribut( "num" ), uIntTmp ) )
	    warning() << "Ne peut pas lire l'attribut \"num\" de "
		      << admise -> balise() << endmsg;
	  else if ( uIntTmp > nbrElement )
	    nbrElement = uIntTmp;

	  RacesAdmisesClasse.back().insert( admise -> donnees() );

	  info() << "Classe admise : " << admise -> donnees() << endmsg;
	  
	  admise = Lecture.prochainFrere( admise );
	}
      while ( admise != 0 );

      if ( elementsAttendus != nbrElement + 1 )
	warning() << "Collecté " << nbrElement + 1 << " races, alors"
		  << "que " << elementsAttendus << " étaient attendues"
		  << endmsg;
      else if ( RacesAdmisesClasse.back().size() != elementsAttendus )
	warning() << "Problème de lecture des races admises : "
		  << "on attend " << elementsAttendus << " items, mais "
		  << RacesAdmisesClasse.back().size() << " ont été trouvés"
		  << endmsg;

      nbrElement = 0;

      noeud = Lecture.prochainFrere( noeud );
    }
  while ( noeud != 0 && noeud -> balise() == "RacesAdmises" );

  Prerequis tmp;

  do
    {
      if ( ! lireString( noeud -> attribut( "nombre" ), elementsAttendus ) )
	{
	  error() << "Ne peut pas lire le nombre de prérequis" << endmsg;
	  func() << "lireFichier ==> fin" << endmsg;
	  /*Debug::*/depileNiveau();
	  return StatusCode( false, 0, - CodesErreurs::ErreurLectureAttributUint,
			     "lireFichier : Impossible de lire le nombre d'élément attendus" );
	}
      
      if ( noeud -> attribut( "groupe" ) != "" )
	info() << "Nous lisons le sous groupe numéro"
	       << noeud -> attribut( "groupe" ) << endmsg;

      const ElementDOM *prerequis( Lecture.premierEnfant( noeud ) );
      PrerequisClasse.push_back( vector< Prerequis >() );

      do
	{
	  if ( ! lireString( prerequis -> attribut( "num" ), uIntTmp ) )
	    warning() << "Ne peut pas lire l'attribut \"num\" de "
		      << prerequis -> balise() << endmsg;
	  else if ( uIntTmp > nbrElement )
	    nbrElement = uIntTmp;
	   
	  // warning() << "Numéro : " << prerequis -> attribut( "num" )
	  // 	    << endmsg;

	  sc = lirePrerequis( prerequis );
	  if ( ! sc )
	    {
	      error() << sc.message() << endmsg;
	      func() << "lireFichier ==> fin" << endmsg;
	      /*Debug::*/depileNiveau();
	      return StatusCode( false, 0, - CodesErreurs::ErreurConstructionObjetComplexe,
				 "lireFichier : impossible d'initialiser les prérequis" );
	    }
	  info() << PrerequisClasse.back().back() << endmsg;

	  prerequis = Lecture.prochainFrere( prerequis );
	}
      while ( prerequis != 0 );

      if ( nbrElement + 1 != elementsAttendus )
	warning() << "Collecté " << nbrElement + 1 << " prérequis, alors "
		  << "que " << elementsAttendus << " étaient attendus"
		  << endmsg;
      else if ( PrerequisClasse.back().size() != elementsAttendus )
	warning() << "Problème de lecture des prérequis : "
		  << "on attend " << elementsAttendus << " items, mais "
		  << PrerequisClasse.back().size() << " ont été trouvés" << endmsg;
      
      nbrElement = 0;
      
      noeud = Lecture.prochainFrere( noeud );
    }
  while ( noeud -> balise() == "Limites" );

  const ElementDOM* comp;
  CompetenceClasse tmpComp;

  if ( noeud -> balise() != "Competences" )
    return StatusCode( false, 0, - CodesErreurs::AutreBaliseTrouvee,
		       "lireFichier : on attendait \"Competences\", mais on trouve " +
		       noeud -> balise() );

  do
    {
      if ( noeud -> attribut( "groupe" ) != "" )
	info() << "Nous lisons le sous groupe numéro"
	       << noeud -> attribut( "groupe" ) << endmsg;

      if ( ! lireString( noeud -> attribut( "nombre" ), elementsAttendus ) )
	{
	  error() << "Ne peut pas lire le nombre de compétences" << endmsg;
	  func() << "lireFichier ==> fin" << endmsg;
	  /*Debug::*/depileNiveau();
	  return StatusCode( false, 0, - CodesErreurs::ErreurLectureAttributUint,
			     "lireFichier : Impossible de lire le nombre d'élément attendus" );
	}

      comp = Lecture.premierEnfant( noeud );

      ListesCompetences.push_back( vector< CompetenceClasse >() );
      
      do
	{
	  if ( ! lireString( comp -> attribut( "num" ), uIntTmp ) )
	    warning() << "Ne peut pas lire l'attribut \"num\" de "
		      << comp -> balise() << endmsg;
	  else if ( uIntTmp > nbrElement )
	    nbrElement = uIntTmp;
	  // info() << "Numero : " << comp -> attribut( "num" )
	  // 		<< endmsg;

	  sc = lireComp( comp, ListesCompetences.back() );
	  if ( ! sc )
	    {
	      error() << sc.message() << endmsg;
	      func() << "lireFichier ==> fin" << endmsg;
	      /*Debug::*/depileNiveau();
	      return StatusCode( false, 0, - CodesErreurs::ErreurConstructionObjetComplexe,
				 "lireFichier : impossible d'initialiser les compétences" );
	    }

	  info() << ListesCompetences.back().back() << endmsg;

	  comp = Lecture.prochainFrere( comp );
	}
      while ( comp != 0 );

      if ( ListesCompetences.back().size() != nbrElement + 1 )
	warning() << "Problème de lecture des compétences : "
		  << nbrElement + 1 << " items ont été lus, mais "
		  << ListesCompetences.back().size() << " ont été enregistrés"
		  << endmsg;
      else if ( nbrElement + 1 != elementsAttendus )
	warning() << "Problème de lecture des compétences : "
		  << "on attend " << elementsAttendus << " items, mais "
		  << nbrElement + 1 << " ont été trouvés" << endmsg;
	
  
      nbrElement = 0;

      noeud = Lecture.prochainFrere( noeud );
    }
  while ( noeud != 0 && noeud -> balise() == "Competences" );

  if ( noeud != 0 && noeud -> balise() == "Restrictions" )
    {
      if ( ! lireString( noeud -> attribut( "nombre" ), elementsAttendus ) )
	{
	  error() << "Ne peut pas lire le nombre de restrictions"
		  << endmsg;
	  func() << "lireFichier ==> fin" << endmsg;
	  /*Debug::*/depileNiveau();
	  return StatusCode( false, 0, - CodesErreurs::ErreurLectureAttributUint,
			     "lireFichier : Impossible de lire le nombre d'élément attendus" );
	}

      comp = Lecture.premierEnfant( noeud );
      
      do
	{
	  if ( ! lireString( comp -> attribut( "num" ), uIntTmp ) )
	    warning() << "Ne peut pas lire l'attribut \"num\" de "
		      << comp -> balise() << endmsg;
	  else if ( uIntTmp > nbrElement )
	    nbrElement = uIntTmp;
	  // info() << "Numero : " << comp -> attribut( "num" )
	  // 		<< endmsg;

	  sc = lireComp( comp, ListeRestrictions );
	  if ( ! sc )
	    {
	      error() << sc.message() << endmsg;
	      func() << "lireFichier ==> fin" << endmsg;
	      /*Debug::*/depileNiveau();
	      return StatusCode( false, 0, - CodesErreurs::ErreurConstructionObjetComplexe,
				 "lireFichier : impossible d'initialiser les restrictions" );
	    }

	  info() << ListeRestrictions.back() << endmsg;

	  comp = Lecture.prochainFrere( comp );
	}
      while ( comp != 0 );

      if ( ListeRestrictions.size() != nbrElement + 1 )
	warning() << "Problème de lecture des restrictions : "
		  << "on attend " << nbrElement + 1 << " items, mais "
		  << ListeRestrictions.size() << " ont été trouvés" << endmsg;
  
      nbrElement = 0;
      
      noeud = Lecture.prochainFrere( noeud );
    }

  if ( noeud != 0 && noeud -> balise() == "EnsemblesComp" )
    {
      if ( ! lireString( noeud -> attribut( "nombre" ), elementsAttendus ) )
	{
	  error() << "Ne peut pas lire le nombre d'ensembles de compétences"
		  << endmsg;
	  func() << "lireFichier ==> fin" << endmsg;
	  /*Debug::*/depileNiveau();
	  return StatusCode( false, 0, - CodesErreurs::ErreurLectureAttributUint,
			     "lireFichier : Impossible de lire le nombre d'élément attendus" );
	}

      const ElementDOM* ens( Lecture.premierEnfant( noeud ) );

      do
	{
	  if ( ! lireString( ens -> attribut( "num" ), uIntTmp ) )
	    warning() << "Ne peut pas lire l'attribut \"num\" de "
		      << comp -> balise() << endmsg;
	  else if ( uIntTmp > nbrElement )
	    nbrElement = uIntTmp;
	  // warning() << "Numero : " << comp -> attribut( "num" )
	  // 		<< endmsg;
	  
	  sc = lireEnsemble( ens );
	  if ( ! sc )
	    {
	      error() << sc.message() << endmsg;
	      func() << "lireFichier ==> fin" << endmsg;
	      /*Debug::*/depileNiveau();
	      return StatusCode( false, 0, - CodesErreurs::ErreurConstructionObjetComplexe,
				 "lireFichier : impossible d'initialiser les ensembles raciaux" );
	    }
	  
	  info() << EnsemblesCompetences.back() << endmsg;
	  
	  ens = Lecture.prochainFrere( ens );
	}
      while ( ens != 0 );

      if ( elementsAttendus != nbrElement + 1 )
	warning() <<  "Collecté " << nbrElement + 1 << " ensembles, alors"
		  << "que " << elementsAttendus << " étaient attendues"
		  << endmsg;
      else if ( EnsemblesCompetences.size() != elementsAttendus )
	warning() << "Problème de lecture des ensembles de compétences  : "
		  << "on attend " << elementsAttendus << " items, mais "
		  << EnsemblesCompetences.size() << " ont été trouvés" << endmsg;
      
      nbrElement = 0;
      
      noeud = Lecture.prochainFrere( noeud );
    }

  NombreTotalCompetences = ListesCompetences[0].size();
  NombreTotalRestrictions = ListeRestrictions.size();
  NombreTotalEnsembles = EnsemblesCompetences.size();

  func() << "lireFichier ==> fin" << endmsg;
  /*Debug::*/depileNiveau();
  return sc;
}

/**
 * Lit un Prerequis depuis le fichier. L'élément de l'arbre DOM passé
 * en argument doit pointer sur une balise "Prerequis". La méthode lit
 * alors le numéro du trait, la valeur minimal (obligatoire) et
 * l'éventuelle valeur maximale.
 *
 * @param[in] elDom pointeur sur l'ElementDOM dont la balise est
 * "Prerequis".
 *
 * @return StatusCode, en cas d'erreur le message associé permet de
 * comprendre quelle est l'erreur rencontrée.
 */
StatusCode MetaClasse::lirePrerequis(const ElementDOM* elDom)
{
  unsigned trait, min, max;

  const ElementDOM* enfant( Lecture.premierEnfant( elDom ) );

  if ( ! lireString( enfant -> donnees(), trait ) )
    return StatusCode( false, 0, - CodesErreurs::ErreurLectureScalaireUint,
		       "lirePrerequis : trait illisible" );

  enfant = Lecture.prochainFrere( enfant );

  if ( ! lireString( enfant -> donnees(), min ) )
    return StatusCode( false, 0, - CodesErreurs::ErreurLectureScalaireUint,
		       "lirePrerequis : min illisible" );

  enfant = Lecture.prochainFrere( enfant );

  if ( enfant != 0 )
    {
      if ( ! lireString( enfant -> donnees(), max ) )
	return StatusCode( false, 0, - CodesErreurs::ErreurLectureScalaireUint,
			   "lirePrerequis : max illisible" );

      PrerequisClasse.back().push_back( Prerequis( trait, min, max ) );
    }
  else
    PrerequisClasse.back().push_back( Prerequis( trait, min ) );

  return StatusCode();
}

/**
 * Lit une compétence / restriction depuis le fichier. L'élément de
 * l'arbre DOM passé en argument doit pointer sur une balise
 * "Competence" ou "Restriction". La méthode lit alors le nom de la
 * compétence, l'éventuel alias et le coût.
 *
 * @param[in] elDom pointeur sur l'ElementDOM dont la balise est
 * "Competence" ou "Restriction".
 * @param[out] vec référence sur le conteneur dans lequel sera stocké
 * la nouvelle instance de CompetenceClasse.
 *
 * @return StatusCode, en cas d'erreur le message associé permet de
 * comprendre quelle est l'erreur rencontrée.
 */
StatusCode MetaClasse::lireComp(const ElementDOM* elDom,
				vector< CompetenceClasse >& vec)
{
  string nom, alias( "" );
  unsigned prix;

  const ElementDOM* enfant( Lecture.premierEnfant( elDom ) );

  nom = enfant -> donnees();

  enfant = Lecture.prochainFrere( enfant );

  if ( enfant -> balise() == "Alias" )
    {
      alias = enfant -> donnees();
      enfant = Lecture.prochainFrere( enfant );
    }

  if ( ! lireString( enfant -> donnees(), prix ) )
    return StatusCode( false, 0, CodesErreurs::ErreurLectureScalaireUint,
		       "lireComp : coût illisible" );

  enfant = Lecture.prochainFrere( enfant );

  if ( enfant == 0 )
    {
      vec.push_back( CompetenceClasse( nom, prix ) );
      if ( alias != "" )
	vec.back().setAliasNom( alias );
      return StatusCode();
    }

  
  if ( enfant -> balise() == "ValeurBase" )
    {
      unsigned valBase;

      if ( ! lireString( enfant -> donnees(), valBase ) )
	return StatusCode( false, 0, CodesErreurs::ErreurLectureScalaireUint,
			   "lireComp : valeur de base illisible" );

      vec.push_back( CompetenceClasse( nom, prix, true, valBase ) );
      if ( alias != "" )
	vec.back().setAliasNom( alias );
      return StatusCode();
    }


  if ( enfant -> balise() == "ScoreAdditionnel" )
    {
      ScoreAdditionnel add( Normal );

      if ( enfant -> donnees() == "Basique" )
	add = Basique;
      else if ( enfant -> donnees() == "Pourcentage" )
	add = Basique;

      vec.push_back( CompetenceClasse( nom, prix, add ) );
      if ( alias != "" )
	vec.back().setAliasNom( alias );
      return StatusCode();
    }

  if ( enfant -> balise() == "SousCategorie" )
    {
      unsigned sousCat, adresseBase;

      if ( ! lireString( enfant -> donnees(), sousCat ) )
	return StatusCode( false, 0, CodesErreurs::ErreurLectureScalaireUint,
			   "lireComp : sous catégorie impossible" );

      enfant = Lecture.prochainFrere( enfant );
      
      if ( enfant == 0 )
	return StatusCode( false, 0, CodesErreurs::ElementManquant,
			   "lireComp : champ AdresseBase attendu" );

      if ( ! lireString( enfant -> donnees(), adresseBase ) )
	return StatusCode( false, 0, CodesErreurs::ErreurLectureScalaireUint,
			   "lireComp : adresse de base illisible" );

      enfant = Lecture.prochainFrere( enfant );

      if ( enfant == 0 )
	{
	  vec.push_back( CompetenceClasse( nom, prix, adresseBase, sousCat ) );
	  if ( alias != "" )
	    vec.back().setAliasNom( alias );
	  return StatusCode();
	}

      if ( enfant -> balise() != "Exclusif" )
	return StatusCode( false, 0, CodesErreurs::AutreBaliseTrouvee,
			   "lireComp : champ Exclusif attendu" );

      vec.push_back( CompetenceClasse( nom, prix, adresseBase, sousCat,
			       enfant -> donnees() == "true" ) );
	  if ( alias != "" )
	    vec.back().setAliasNom( alias );
	  return StatusCode();
    }
  
  return StatusCode();
}

/**
 * Lit un ensemble de compétences depuis le fichier. L'élément de
 * l'arbre DOM passé en argument doit pointer sur une balise
 * "Ensemble". On lit le nom de l'ensemble, le coût, les membres. Les
 * champs additionnels sont le numéro de l'ensemble des races admises,
 * le numéro de l'ensemble des prérequis et un booléen indiquant si le
 * choix est obligatoire (càd il \e faut choisir un ensemble, comme
 * pour les Spécialistes). Dans le cas où ces champs ne sont pas
 * présent, leur valeur par défaut sont 0, 0 et \e false
 * respectivement (ce qui signifie que tous les ensembles sont soumis
 * au même prérequis raciaux et sur les traits, et que la sélection
 * est optionnelle).
 *
 * @param[in] elDom pointeur sur l'ElementDOM dont la balise est
 * "Ensemble".
 *
 * @return StatusCode, en cas d'erreur le message permet de
 * comprendre quelle est l'erreur rencontrée.
 */
StatusCode MetaClasse::lireEnsemble(const ElementDOM* elDom)
{
  string nom;
  unsigned prix;
  vector< unsigned > membresEns, exclusEns;

  const ElementDOM* enfant( Lecture.premierEnfant( elDom ) );

  nom = enfant -> donnees();

  enfant = Lecture.prochainFrere( enfant );
  
  if ( ! lireString( enfant -> donnees(), prix ) )
    return StatusCode( false, 0, CodesErreurs::ErreurLectureScalaireUint,
		       "lireEnsemble : coût illisible" );

  enfant = Lecture.prochainFrere( enfant );

  if ( ! lireVectorUInt( enfant -> donnees(), &membresEns ) )
    return StatusCode( false, 0, CodesErreurs::ErreurLectureVecteurUint,
		       "lireEnsemble : membres illisibles" );

  enfant = Lecture.prochainFrere( enfant );
  
  if ( enfant == 0 )
    EnsemblesCompetences.push_back( EnsembleCompetencesClasse( nom, prix, membresEns ) );

  if ( enfant -> balise() == "Exclus" )
    {
       if ( ! lireVectorUInt( enfant -> donnees(), &exclusEns ) )
	 return StatusCode( false, 0, CodesErreurs::ErreurLectureVecteurUint,
			    "lireEnsemble : exclus illisibles" );
       
       enfant = Lecture.prochainFrere( enfant );
    }

  unsigned numRace( 0 ), numPrer( 0 );
  string booleen( "" );

  if ( enfant != 0 && enfant -> balise() == "ListeRaces" )
    {
      if ( ! lireString( enfant -> donnees(), numRace ) )
	return StatusCode( false, 0, CodesErreurs::ErreurLectureVecteurUint,
			   "lireEnsemble : numéro des races admises illisible" );

      enfant = Lecture.prochainFrere( enfant );
    }

  if ( enfant != 0 && enfant -> balise() == "ListePrerequis" )
    {
      if ( ! lireString( enfant -> donnees(), numPrer ) )
	return StatusCode( false, 0, CodesErreurs::ErreurLectureScalaireUint,
			   "lireEnsemble : numéro des prerequis illisible" );

      enfant = Lecture.prochainFrere( enfant );
    }

  if ( enfant != 0 && enfant -> balise() == "ChoixObligatoire" )
    {
      booleen = enfant -> donnees();
    }

  EnsemblesCompetences.push_back( EnsembleCompetencesClasse( nom, prix, membresEns, exclusEns,
					       numRace, numPrer,
					       booleen == "true" ) );
  
  return StatusCode();
}

/**
 * Crée un objet de la bonne classe à partir de l'instance
 * courante. Attention, cette méthode alloue de la mémoire pour cela,
 * ne pas oublier le \c delete !
 *
 * @code
 * MetaClasse patron( "Druide" );
 * StatusCode sc( patron.lireFichier() );
 * if ( ! sc )
 * {
 *     error() << "On a un problème!" << endmsg;
 *     exit( 1 );
 * }
 * Classe *ptr = patron.creerObjet();
 *
 * [...]
 * delete ptr;
 * @endcode
 *
 * @return un pointeur sur Classe dont la mémoire est allouée ici, qui
 * est copiée depuis l'instance courante.
 */
Classe* MetaClasse::creerObjet()
{
  if ( NomClasse == "Guerrier" )
    return new Guerrier( this );
  else if ( NomClasse == "Rôdeur" )
    return new Rodeur( this );
  else if ( NomClasse == "Paladin" )
    return new Paladin( this );
  else if ( NomClasse == "Clerc" )
    return new Clerc( this );
  else if ( NomClasse == "Druide" )
    return new Druide( this );
  else if ( NomClasse == "Mage" )
    return new Mage( this );
  else if ( NomClasse == "Spécialiste" )
    return new Specialiste( this );
  else if ( NomClasse == "Barde" )
    return new Barde( this );
  else if ( NomClasse == "Voleur" )
    return new Voleur( this );
  else
    return 0;
}

unsigned MetaClasse::aSpecialisation() const
{
  warning() << "Tentative de demander si spécialisation disponible pour MetaClasse" << endmsg;

  return -1u;
}
